package goods

import (
	"context"

	"shop-manager/internal/do"

	"github.com/samber/lo"
	"gorm.io/gorm"
)

//go:generate newc
type GoodsRepo struct {
	db *gorm.DB
}

func (c *GoodsRepo) CreateGoods(ctx context.Context, oneData *do.Goods) (int, error) {
	if err := c.db.WithContext(ctx).Create(&oneData).Error; err != nil {
		return 0, err
	}
	return oneData.ID, nil
}

func (c *GoodsRepo) BatchCreateGoods(ctx context.Context, listData []*do.Goods) error {
	if err := c.db.WithContext(ctx).Create(&listData).Error; err != nil {
		return err
	}
	return nil
}

func (c *GoodsRepo) UpdateGoods(ctx context.Context, oneData *do.Goods) error {
	origin := &do.Goods{}
	if err := c.db.WithContext(ctx).First(origin, oneData.ID).Error; err != nil {
		return err
	}

	// 目前可修改以下字段
	origin.BarCode = oneData.BarCode
	origin.Name = oneData.Name
	origin.FactoryPrice = oneData.FactoryPrice
	origin.CategoryID = oneData.CategoryID
	origin.BoxRule = oneData.BoxRule

	return c.db.WithContext(ctx).Save(&origin).Error
}

func (c *GoodsRepo) DeleteGoods(ctx context.Context, id int) error {
	return c.db.WithContext(ctx).Delete(&do.Goods{}, id).Error
}

type ListGoodsParam struct {
	Page     int
	PageSize int

	IDs      []int
	BarCodes []string
	Search   string // 模糊搜索: Name

	// 按分类查询
	ClassID int // 类目
	LevelID int // 等级
	BrandID int // 品牌

	// 排序, 格式为 field [ASC/DESC], ...
	Sorts string

	OnlyUncategorized bool
}

// 分页查询
func (repo *GoodsRepo) ListGoods(ctx context.Context, param ListGoodsParam) (listData []*do.Goods, total int64, err error) {
	if param.Page <= 0 {
		param.Page = 1
	}
	if param.PageSize == 0 { // -1 means no limit
		param.PageSize = do.DefaultPageSize
	}

	err = repo.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		tx = tx.Model(&do.Goods{}).Table("goods AS g")

		if param.ClassID > 0 || param.LevelID > 0 || param.BrandID > 0 {
			sql := `INNER JOIN category_meta m ON (
				(c.class_id = m.id AND m.category_type = ?) OR
				(c.level_id = m.id AND m.category_type = ?) OR
				(c.brand_id = m.id AND m.category_type = ?)) `
			args := []interface{}{do.CategoryType_Class, do.CategoryType_Level, do.CategoryType_Brand}

			tx.Joins(sql, args...)
		}
		if len(param.BarCodes) > 0 {
			tx.Where("g.bar_code IN ?", param.BarCodes)
		}
		if param.Search != "" {
			tx.Where("g.name like ?", "%"+param.Search+"%")
		}
		if param.OnlyUncategorized {
			tx.Where("g.category_id = ?", do.CategoryTobeDone)
		}
		if len(param.IDs) > 0 {
			tx.Where("g.id IN ?", param.IDs)
		}

		err = tx.Count(&total).Error
		if err != nil {
			return err
		}

		if param.Sorts != "" {
			tx = tx.Order(param.Sorts)
		}
		err = tx.Model(&do.Goods{}).Find(&listData).Count(&total).Error
		if err != nil {
			return err
		}

		offset := (param.Page - 1) * param.PageSize
		err = tx.
			Offset(offset).
			Limit(param.PageSize).
			Find(&listData).Error
		if err != nil {
			return err
		}

		return err
	})

	return listData, total, err
}

// get goods map
func (repo *GoodsRepo) GetGoodsMap(ctx context.Context, param ListGoodsParam) (map[int]*do.Goods, error) {
	listData, _, err := repo.ListGoods(ctx, param)
	if err != nil {
		return nil, err
	}
	return lo.SliceToMap(listData, func(v *do.Goods) (int, *do.Goods) {
		return v.ID, v
	}), nil
}
